﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DataModel
{
    public class DataAccess : IDataAccess
    {
        #region Private Field

        // repositories for pattern model, pattern instance and pattern location
        PatternTemplateRepository patternTemplateRepository;
        PatternInstanceRepository patternInstanceRepository;
        PatternLocationRepository patternLocationRepository;

        #endregion

        #region Constructors

        public DataAccess()
        {
            // initiate repositories
            patternTemplateRepository = new PatternTemplateRepository();
            patternInstanceRepository = new PatternInstanceRepository();
            patternLocationRepository = new PatternLocationRepository();
        }

        #endregion

        #region Public Methods

        public IEnumerable<PatternTemplate> GetAllPatternTemplates()
        {
            return patternTemplateRepository.GetAll();
        }

        public IEnumerable<PatternTemplate> GetAllPatternTemplates(string patternLibDirectoryPath)
        {
            var location = patternLocationRepository.First(loc => loc.Path == patternLibDirectoryPath);
            var patternTemplates = new List<PatternTemplate>();

            if (location != null)
            {
                foreach (var patternInstance in location.PatternInstances)
                {
                    if (!patternTemplates.Contains(patternInstance.PatternTemplate))
                    {
                        patternTemplates.Add(patternInstance.PatternTemplate);
                    }
                } 
            }

            return patternTemplates;
        }

        public void AddPatternTemplate(PatternTemplate patternTemplate)
        {
            // add pattern model
            patternTemplateRepository.Add(patternTemplate);

            // commit to database
            patternTemplateRepository.SaveChanges();
        }

        public void RemovePatternTemplate(int patternTemplateID)
        {
            patternTemplateRepository.Delete(template => template.ID == patternTemplateID);
            patternTemplateRepository.SaveChanges();
        }

        public void DeletePatternTemplate(PatternTemplate patternTemplate)
        {
            // delete the pattern model
            patternTemplateRepository.Delete(patternTemplate);

            // commit to database
            patternTemplateRepository.SaveChanges();
        }

        public IEnumerable<PatternInstance> GetAllPatternInstances()
        {
            throw new NotImplementedException();
        }

        public void AddPatternInstance(PatternInstance patternInstance)
        {
            // add pattern instancce
            patternInstanceRepository.Add(patternInstance);

            // commit to database
            patternInstanceRepository.SaveChanges();
        }

        public bool SavePatternTemplate(int ID, string name = null, string comments = null)
        {
            // check if there is any existing template having the same name
            var existingPatternTemplate = patternTemplateRepository.First(template => template.Name == name);
            if (existingPatternTemplate != null)
            {
                return false;
            }

            // retrieve the pattern template with specified ID from the database
            var updatedPatternTemplate = patternTemplateRepository.Single(template => template.ID == ID);

            // update information
            if (name != null) updatedPatternTemplate.Name = name;
            if (comments != null) updatedPatternTemplate.Comments = comments;

            // commit changes to the DB
            patternTemplateRepository.SaveChanges();

            return true;
        }

        public void UpdateTemplateComment(int patternTemplateID, string templateComments)
        {
            // retrieve the pattern template with specified ID from the database
            var updatedPatternTemplate = patternTemplateRepository.Single(template => template.ID == patternTemplateID);

            // update information
            if (templateComments != null) updatedPatternTemplate.Comments = templateComments;

            // commit changes to the DB
            patternTemplateRepository.SaveChanges();
        }

        public bool SavePatternInstance(int ID, string name = null, string comments = null)
        {
            // check if there is any existing instance having the same name
            var existingPatternInstance = patternInstanceRepository.First(instance => instance.Name == name);
            if (existingPatternInstance != null)
            {
                return false;
            }

            // retrieve the pattern instance with specified ID from the database
            var updatedPatternInstance = patternInstanceRepository.Single(instance => instance.ID == ID);

            // update information
            if (name != null) updatedPatternInstance.Name = name;
            if (comments != null) updatedPatternInstance.Comments = comments;

            // commit changes to the DB
            patternInstanceRepository.SaveChanges();

            return true;
        }

        public void UpdateInstanceComment(int patternInstanceID, string instanceComments = null)
        {
            // retrieve the pattern template with specified ID from the database
            var updatedPatternInstance = patternInstanceRepository.Single(instance => instance.ID == patternInstanceID);

            // update information
            if (instanceComments != null) updatedPatternInstance.Comments = instanceComments;

            // commit changes to the DB
            patternInstanceRepository.SaveChanges();
        }

        public void DeletePatternInstance(PatternInstance patternModel)
        {
            throw new NotImplementedException();
        }

        public void RemovePatternInstance(int patternInstanceID)
        {
            // remove all instance - location entry
            patternInstanceRepository.RemoveInstanceLocationEntry(patternInstanceID);

            // remove pattern instance
            patternInstanceRepository.Delete(instance => instance.ID == patternInstanceID);

            // commit changes to the DB
            patternInstanceRepository.SaveChanges();
        }

        public IEnumerable<PatternLocation> GetAllPatternLocations()
        {
            return patternLocationRepository.GetAll();
        }

        public void AddPatternLocation(PatternLocation patternLocation)
        {
            // add pattern location
            patternLocationRepository.Add(patternLocation);

            // commit changes to the DB
            patternLocationRepository.SaveChanges();
        }

        public void AddPatternLocation(string locationName, string locationPath)
        {
            // return if the location already exists
            var patternLocations = GetAllPatternLocations();
            foreach (var location in patternLocations)
            {
                if (location.Path == locationPath || location.Name == locationName)
                {
                    return;
                }
            }

            // create new location with specified path
            var patternLocation = new PatternLocation
            {
                Name = locationName,
                Path = locationPath
            };

            AddPatternLocation(patternLocation);
        }

        public bool IsInstanceInLocation(int patternInstanceID, string locationPath)
        {
            var patternInstance = patternInstanceRepository.First(instance => instance.ID == patternInstanceID);

            if (patternInstance == null)
            {
                return false;
            }

            foreach (var location in patternInstance.PatternLocations)
            {
                if (location.Path == locationPath)
                {
                    return true;
                }
            }

            return false;
        }

        public void AssignInstanceToLocation(int patternInstanceID, string locationPath)
        {
            // get pattern instance
            var patternInstance = patternInstanceRepository.First(instance => instance.ID == patternInstanceID);
            if (patternInstance == null)
            {
                return;
            }

            // get location
            var location = patternLocationRepository.First(loc => loc.Path == locationPath);
            if (location == null)
            {
                return;
            }
            
            // set relationship
            patternLocationRepository.AssignInstanceToLocation(patternInstance.ID, location.ID);
            patternLocationRepository.SaveChanges();
        }

        public void RemoveInstanceFromLocation(int patternInstanceID, string locationPath)
        {
            // get pattern instance
            var patternInstance = patternInstanceRepository.First(instance => instance.ID == patternInstanceID);
            if (patternInstance == null)
            {
                return;
            }

            // get location
            var location = patternLocationRepository.First(loc => loc.Path == locationPath);
            if (location == null)
            {
                return;
            }

            // remove relationship
            patternLocationRepository.RemoveInstanceFromLocation(patternInstance.ID, location.ID);
            patternLocationRepository.SaveChanges();
        }

        public IEnumerable<PatternLocation> GetAllPatternLocations(int patternInstanceID)
        {
            // get the pattern instance
            var patternInstance = patternInstanceRepository.First(instance => instance.ID == patternInstanceID);

            // return the list of locations
            return patternInstance.PatternLocations;
        }

        #endregion
    }
}
